package com.xyxy.platform.modules.core.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;

/**
 * 文件处理常用工具类
 * Time: 上午10:31
 * To change this template use File | Settings | File Templates.
 */
public class Files3 {

    protected static Logger logger = LoggerFactory.getLogger(Files3.class);

    /**
     * 判断文件的编码格式
     *
     * @param file :file
     * @return 文件编码格式
     */
    public static String getFileEncoding(File file) {
        String charset = "GBK"; // 默认编码
        byte[] first3Bytes = new byte[3];
        BufferedInputStream inputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(file));
            inputStream.mark(0);
            int read = inputStream.read(first3Bytes, 0, 3);
            if (read == -1) return charset;
            if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
                return "UTF-16LE";
            } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {
                return "UTF-8";
            }
            inputStream.reset();
            while ((read = inputStream.read()) != -1) {
                if (read >= 0xF0) break;
                //单独出现BF以下的，也算是GBK
                if (0x80 <= read && read <= 0xBF)
                    break;
                if (0xC0 <= read && read <= 0xDF) {
                    read = inputStream.read();
                    if (0x80 <= read && read <= 0xBF)// 双字节 (0xC0 - 0xDF)
                        // (0x80 -0xBF),也可能在GB编码内
                        continue;
                    else
                        break;
                    // 也有可能出错，但是几率较小
                } else if (0xE0 <= read && read <= 0xEF) {
                    read = inputStream.read();
                    if (0x80 <= read && read <= 0xBF) {
                        read = inputStream.read();
                        if (0x80 <= read && read <= 0xBF) {
                            charset = "UTF-8";
                            break;
                        } else
                            break;
                    } else
                        break;
                }
            }
        } catch (Exception e) {
            logger.error("处理文件出现异常:{}",
                    file.getAbsolutePath(), e);
        } finally {
            closeQuietly(inputStream);
        }
        return charset;
    }

    public static void closeQuietly(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            } catch (Exception e) {
                logger.error("关闭文件出现异常", e);
            }
        }
    }


    /**
     * 释放NIOMap文件句柄
     *
     * @param buffer 文件缓冲
     * @throws IOException
     */
    public static void freeMMapBuffer(final ByteBuffer buffer) throws IOException {
        if (null == buffer) {
            return;
        }
        try {
            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                @Override
                public Void run() throws Exception {
                    final Method getCleanerMethod = buffer.getClass()
                            .getMethod("cleaner");
                    getCleanerMethod.setAccessible(true);
                    final Object cleaner = getCleanerMethod.invoke(buffer);
                    if (cleaner != null) {
                        cleaner.getClass().getMethod("clean")
                                .invoke(cleaner);
                    }
                    return null;
                }
            });
        } catch (PrivilegedActionException e) {
            final IOException ioe = new IOException("unable to unmap the mapped buffer");
            ioe.initCause(e.getCause());
            throw ioe;
        }
    }

    /**
     * 复制文件, 使用文件通道的方式(性能比传统的缓冲输入输出流效率高三分之一)
     *
     * @param source 源文件
     * @param target 复制到的新文件
     */
    public static void copy(File source, File target) {
        FileInputStream fi = null;
        FileOutputStream fo = null;
        FileChannel in = null;
        FileChannel out = null;
        try {
            fi = new FileInputStream(source);
            fo = new FileOutputStream(target);
            //得到对应的文件通道
            in = fi.getChannel();
            //得到对应的文件通道
            out = fo.getChannel();
            //连接两个通道，并且从in通道读取，然后写入out通道
            in.transferTo(0, in.size(), out);
        } catch (IOException e) {
            logger.error("使用文件通道的方式复制文件失败, e: {}", e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                fi.close();
                in.close();
                fo.close();
                out.close();
            } catch (IOException e) {
                logger.error("使用文件通道的方式复制文件, 释放资源失败, e: {}", e.getMessage());
            }
        }
    }
}
